﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Linq;
using System.Xml.XPath;
using UtilZ.Dotnet.Ex.Base;

namespace UtilZ.Dotnet.Ex.Config
{
    /// <summary>
    /// 应用程序配置文件管理器
    /// </summary>
    public class AppConfigurationManager
    {
        /// <summary>
        /// 加载主程序应用程序配置文件
        /// </summary>
        /// <returns>应用程序配置</returns>
        public static AppConfiguration Load()
        {
            string configFilePath;
#if CORE
            configFilePath = $"{EnvironmentEx.AppName}.dll.config";
#else
            configFilePath =$"{EnvironmentEx.AppName}.exe.config";
#endif

            return new AppConfiguration(configFilePath);
        }

        /// <summary>
        /// 加载指定应用程序配置文件
        /// </summary>
        /// <param name="configFilePath">指定应用程序配置文件路径</param>
        /// <returns>应用程序配置</returns>
        public static AppConfiguration Load(string configFilePath)
        {
            return new AppConfiguration(configFilePath);
        }
    }


    /// <summary>
    /// 应用程序配置
    /// </summary>
    public class AppConfiguration
    {
        /// <summary>
        /// 应用程序配置文件路径
        /// </summary>
        public readonly string ConfigFilePath;

        /// <summary>
        /// 配置项键值集合
        /// </summary>
        public readonly Dictionary<string, string> AppSettings;

        /// <summary>
        /// 构造函数初始化
        /// </summary>
        /// <param name="configFilePath">应用程序配置文件路径</param>
        /// <exception cref="ArgumentNullException">应用程序配置文件路径为空或null异常</exception>
        /// <exception cref="FileNotFoundException">应用程序配置文件路径不存在异常</exception>
        public AppConfiguration(string configFilePath)
        {
            this.AppSettings = Load(configFilePath);
            this.ConfigFilePath = configFilePath;
        }

        private static Dictionary<string, string> Load(string configFilePath)
        {
            if (string.IsNullOrWhiteSpace(configFilePath))
            {
                throw new ArgumentNullException(nameof(configFilePath), "配置文件路径不为空或全空格.");
            }

            //assemblyName
            if (string.IsNullOrWhiteSpace(Path.GetPathRoot(configFilePath)))
            {
                configFilePath = PathEx.Combine(PathType.Local, PathEx.GetEntryAssemblyDirectory(), configFilePath);
            }

            if (!File.Exists(configFilePath))
            {
                throw new FileNotFoundException("配置文件不存在", configFilePath);
            }

            var xdoc = XDocument.Load(configFilePath);
            var addEles = xdoc.XPathSelectElements(@"configuration/appSettings/add");
            var appSettings = new Dictionary<string, string>();
            string key, value;

            foreach (var addEle in addEles)
            {
                //<add key="SingleInstanceRun" value="true"/>
                key = XmlEx.GetXElementAttributeValue(addEle, "key");
                if (key == null)
                {
                    throw new ArgumentException(@"configuration/appSettings/add中的key必须存在");
                }
                value = XmlEx.GetXElementAttributeValue(addEle, "value");
                appSettings[key] = value;
            }

            return appSettings;
        }

        /// <summary>
        /// 尝试获取指定key的值,并转换为特定类型[获取成功返回true,失败返回false]
        /// </summary>
        /// <typeparam name="TResult">值类型</typeparam>
        /// <param name="key">指定的key</param>
        /// <param name="value">值</param>
        /// <returns>获取成功返回true,失败返回false</returns>
        public bool TryGetValue<TResult>(string key, out TResult value)
        {
            string valueStr;
            if (!this.AppSettings.TryGetValue(key, out valueStr))
            {
                value = default(TResult);
                return false;
            }

            object objRet;
            if (ConvertEx.TryConvertToObject(typeof(TResult), valueStr, out objRet))
            {
                value = (TResult)objRet;
                return true;
            }
            else
            {
                value = default(TResult);
                return false;
            }
        }

        /// <summary>
        /// 获取指定key的值,并转换为特定类型[获取成功返回配置中值,失败返回传入的默认值]
        /// </summary>
        /// <typeparam name="TResult">值类型</typeparam>
        /// <param name="key">指定的key</param>
        /// <param name="defaultValue">默认值</param>
        /// <returns>获取到的值</returns>
        public TResult GetValue<TResult>(string key, TResult defaultValue)
        {
            string valueStr;
            if (!this.AppSettings.TryGetValue(key, out valueStr))
            {
                return defaultValue;
            }

            object objRet;
            if (ConvertEx.TryConvertToObject(typeof(TResult), valueStr, out objRet))
            {
                return (TResult)objRet;
            }
            else
            {
                return defaultValue;
            }
        }
    }

}
